package mxp

import cpu74.ASM74Lexer
import cpu74.ASM74Parser
import org.antlr.v4.runtime.*
import java.io.File
import java.io.FileOutputStream
import java.io.OutputStream
import java.nio.file.Files
import java.nio.file.Paths
import kotlin.system.exitProcess

/*
地址空间分配
* ROM
    * $0000-$3FFF   16K
        * init
            * init sp
            * load data default value
                * longjmp
            * jmp main
        * rodata
        * data default value
    * $4000-$7FFF   16K
        * rom page
* RAM
    * $8000-$80FF   编译器占用
    * $8100-$9FFF   8K stack
    * $A000-$BFFF   8K data
    * $C000-$FFEF   16K heap
    * $FFF0-$FFFE   IO
    * $FFFF         页寄存器
 */

// ROM页区域，根据 PAGE 的值换页
private val ROM_PAGE_START = 0x4000
private val ROM_PAGE_END = 0x7FFF
private val ROM_PAGE_SIZE = ROM_PAGE_END - ROM_PAGE_START + 1

// ROM固定区域，包括初始化代码，RODATA， DATA初始值
private val ROM_FIX_START = 0x0000
private val ROM_FIX_END = 0x3FFF
private val ROM_BASE_SIZE = ROM_FIX_END - ROM_FIX_START + 1

private val RAM_STACK_BASE = 0x9FFF
private val RAM_STACK_LIMIT = 0x8100

private val RAM_DATA_START = 0xA000
private val RAM_DATA_END = 0xBFFF
private val RAM_DATA_SIZE = RAM_DATA_END - RAM_DATA_START + 1

private val RAM_HEAP_START = 0xC000
private val RAM_HEAP_END = 0xFFEF
private val RAM_HEAP_SIZE = RAM_HEAP_END - RAM_HEAP_START + 1

private val model = MCodeDefModel.fromJson(Files.readAllBytes(Paths.get("../mcode.json")).toString(charset("UTF-8")))


private var rom_rodata = Pair(0, 0)
private var ram_rodata = Pair(0, 0)
private var rom_data = Pair(0, 0)
private var ram_data = Pair(0, 0)
private var rom_code = Pair(0, 0)
private var ram_bss = Pair(0, 0)


private var fos: OutputStream? = null

private val globalLabelPageMap = mutableMapOf<String, Int>()
private val globalLabelAddrMap = mutableMapOf<String, Int>()

private val localLabelPageMap = mutableMapOf<ASM74Parser.ProgContext, MutableMap<String, Int>>()
private val localLabelAddrMap = mutableMapOf<ASM74Parser.ProgContext, MutableMap<String, Int>>()

private val localLabelRegex = Regex("L[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]")
private fun isLocalLabel(s: String) = localLabelRegex.matches(s)

private fun setLabelPageAddr(ruleCtx: RuleContext?, name: String, page: Int, addr: Int) {
    if (isLocalLabel(name)) {
        if (ruleCtx == null) {
            println("Fuck")
            exitProcess(1)
        }
        var progCtx = ruleCtx
        while (progCtx!!.parent != null) {
            progCtx = progCtx.parent
        }

        if (progCtx !is ASM74Parser.ProgContext) {
            println("Fuck")
            exitProcess(1)
        }

        localLabelPageMap.getOrPut(progCtx, { mutableMapOf() })[name] = page
        localLabelAddrMap.getOrPut(progCtx, { mutableMapOf() })[name] = addr
    } else {
        globalLabelPageMap[name] = page
        globalLabelAddrMap[name] = addr
    }
}

private fun getLabelAddr(ruleCtx: RuleContext?, name: String): Int {
    return if (isLocalLabel(name)) {
        if (ruleCtx == null) {
            println("Fuck")
            exitProcess(1)
        }

        var progCtx = ruleCtx
        while (progCtx!!.parent != null) {
            progCtx = progCtx.parent
        }

        if (progCtx !is ASM74Parser.ProgContext) {
            println("Fuck")
            exitProcess(1)
        }

        localLabelAddrMap[progCtx]!![name]!!
    } else {
        globalLabelAddrMap[name]!!
    }
}

private fun getLabelPage(ruleCtx: RuleContext, name: String): Int {
    return if (isLocalLabel(name)) {
        var progCtx = ruleCtx
        while (progCtx.parent != null) {
            progCtx = progCtx.parent
        }

        if (progCtx !is ASM74Parser.ProgContext) {
            println("Fuck")
            exitProcess(1)
        }

        localLabelPageMap[progCtx]!![name]!!
    } else {
        globalLabelPageMap[name]!!
    }
}


private abstract class Segment(var progCtx: ASM74Parser.ProgContext?) {
    private companion object {
        var counter = 0
    }

    val id = counter++

    var romPage = 0
    var ramPage = 0

    var ramAddr = 0
    var ramSize = 0

    var romAddr = 0
    var romSize = 0
    val rom = mutableListOf<Int>()

    val romLabelOffsetMap = mutableMapOf<String, Int>()
    val ramLabelOffsetMap = mutableMapOf<String, Int>()


    open fun output_rom(rom: Array<Int>) {
        for (i in 0 until romSize) {
            rom[romPage * 32 * 1024 + romAddr + i] = this.rom[i]
        }
    }
}

private class Data(progCtx: ASM74Parser.ProgContext?) : Segment(progCtx) {
    val romLabelRefMap = mutableMapOf<Int, String>()
    val romLabelSizeMap = mutableMapOf<String, Int>()

    var initProc: Proc? = null

    override fun output_rom(rom: Array<Int>) {
        super.output_rom(rom)
        initProc!!.output_rom(rom)
    }

    fun genInitProc() {
        var counter = 0
        var code = ".segment \"CODE\"\n"
        code += ".proc _init_data$id\n"

        romLabelSizeMap.forEach { (label, size) ->
            if (size <= 16) {
                for (i in 0 until size) {
                    code += """
                        lda _init_${label},#$i
                        sta $label,#$i
                        
                    """.trimIndent()
                }
            } else {
                val label0 = "_init_data${id}_${counter++}"
                val label1 = "_init_data${id}_${counter++}"
                code += """
                    lda #${size and 0xff}
                    sta TMP0
                    lda #${(size shr 8) and 0xff}
                    sta TMP0,#1
                    lda #L(_init_$label)
                    sta TMP1
                    lda #H(_init_$label)
                    sta TMP1,#1
                    lda #L($label)
                    sta TMP2
                    lda #H($label)
                    sta TMP2,#1
           $label0: lda TMP0
                    or TMP0,#1
                    jz $label1
                    lda (TMP1)
                    sta (TMP2)
                    sec
                    lda TMP0
                    sub #1
                    sta TMP0
                    lda TMP0,#1
                    sub #0
                    sta TMP0,#1
                    clc
                    lda TMP1
                    add #1
                    sta TMP1
                    lda TMP1,#1
                    add #0
                    sta TMP1,#1
                    clc
                    lda TMP2
                    add #1
                    sta TMP2
                    lda TMP2,#1
                    add #0
                    sta TMP2,#1
                    jmp $label0
           $label1:

                """.trimIndent()
            }

        }

        this.initProc = parse_proc(listOf(antlr_parse(code)))[0]
        this.initProc!!.progCtx = this.progCtx
    }
}

private class BSS(progCtx: ASM74Parser.ProgContext?) : Segment(progCtx) {
}

private class ROData(progCtx: ASM74Parser.ProgContext?) : Segment(progCtx) {
}

private class Proc(progCtx: ASM74Parser.ProgContext?, val name: String) : Segment(progCtx) {
    val romLabelRefMap = mutableMapOf<Int, MutableList<String>>()
    val lineCtxs = mutableListOf<ASM74Parser.LineContext>()
}

private fun evalExpr2(expr2Ctx: ASM74Parser.Expr2Context, varMap: Map<String, Int>? = null): Int {
    expr2Ctx.getParent()

    if (expr2Ctx.NUMBER() != null) {
        return str2int(expr2Ctx.NUMBER().text)
    } else if (expr2Ctx.ID() != null) {
        if (expr2Ctx.expr() != null) {
            when (expr2Ctx.ID().text) {
                "P" -> {
                    val label = expr2Ctx.expr().text
                    return getLabelPage(expr2Ctx, label)
                }
                "H" -> {
                    return (evalExpr(expr2Ctx.expr()) shr 8) and 0xff
                }
                "L" -> {
                    return evalExpr(expr2Ctx.expr()) and 0xff
                }
            }
        } else {
            val label = expr2Ctx.ID().text
            if (varMap != null) {
                return varMap[label]!!
            } else {
                return getLabelAddr(expr2Ctx, label)
            }

        }
    }

    println("Impossible")
    exitProcess(1)
}

private fun evalExpr1(expr1Ctx: ASM74Parser.Expr1Context, varMap: Map<String, Int>? = null): Int {
    var result = 0
    var op = ""
    for (child in expr1Ctx.children) {
        if (child.javaClass == ASM74Parser.Expr2Context::class.java) {
            if (op == "") {
                result = evalExpr2(child as ASM74Parser.Expr2Context, varMap)
            } else {
                if (op == "*") {
                    result *= evalExpr2(child as ASM74Parser.Expr2Context, varMap)
                } else if (op == "/") {
                    result /= evalExpr2(child as ASM74Parser.Expr2Context, varMap)
                }
            }
        } else {
            op = child.text
        }
    }

    return result
}

private fun evalExpr(exprCtx: ASM74Parser.ExprContext, varMap: Map<String, Int>? = null): Int {
    var result = 0
    var op = ""
    for (child in exprCtx.children) {
        if (child.javaClass == ASM74Parser.Expr1Context::class.java) {
            if (op == "") {
                result = evalExpr1(child as ASM74Parser.Expr1Context, varMap)
            } else {
                if (op == "+") {
                    result += evalExpr1(child as ASM74Parser.Expr1Context, varMap)
                } else if (op == "-") {
                    result -= evalExpr1(child as ASM74Parser.Expr1Context, varMap)
                }
            }
        } else {
            op = child.text
        }
    }

    return result
}

private val AMCtx2NameMap = mapOf(
    ASM74Parser.Am_defaultContext::class.java to "default",
    ASM74Parser.Am_immContext::class.java to "imm",
    ASM74Parser.Am_directContext::class.java to "direct",
    ASM74Parser.Am_indirectContext::class.java to "indirect",
    ASM74Parser.Am_direct_indexContext::class.java to "direct_index",
    ASM74Parser.Am_indirect_indexContext::class.java to "indirect_index",
    ASM74Parser.Am_index_indirectContext::class.java to "index_indirect"
)

private fun getAmName(amCtx: ASM74Parser.AmContext): String? =
    AMCtx2NameMap[(amCtx.children[0] as ParserRuleContext).javaClass]


private fun asm_proc_line(proc: Proc, lineCtx: ASM74Parser.LineContext, addr: Int): ByteArray {
    if (lineCtx.cmd() != null) {
        if (lineCtx.cmd().start.text == "debug") {
            var s = String.format("[%06x]%02x:%04x", proc.romPage * 32 * 1024 + addr, proc.romPage, addr)
            s = s.padEnd(55, ' ')
            s += " DEBUG: " + lineCtx.cmd().ID();
            if (lineCtx.COMMENT() != null) {
                s = s.padEnd(80, ' ')
                s += lineCtx.COMMENT().text
            }
            output_list(s)
            return ByteArray(0)
        }
    }

    val opcode = lineCtx.opcode().text
    val amCtx = lineCtx.am()
    val amName = getAmName(lineCtx.am())
    val bytes = when (amName) {
        "default" ->
            model.getBinaryCode(opcode, "default", 0, 0)
        "imm" ->
            model.getBinaryCode(opcode, "imm", 0, evalExpr(amCtx.am_imm().num().expr()))
        "direct" ->
            model.getBinaryCode(opcode, "direct", evalExpr(amCtx.am_direct().address().expr()), 0)
        "indirect" ->
            model.getBinaryCode(opcode, "indirect", evalExpr(amCtx.am_indirect().address().expr()), 0)
        "indirect_index" -> {
            model.getBinaryCode(
                opcode,
                "indirect_index",
                evalExpr(amCtx.am_indirect_index().address().expr()),
                evalExpr(amCtx.am_indirect_index().num().expr())
            )
        }
        "direct_index" ->
            model.getBinaryCode(
                opcode,
                "direct",
                evalExpr(
                    amCtx.am_direct_index().address().expr()
                ) + evalExpr(amCtx.am_direct_index().num().expr()),
                0
            )
        "index_indirect" ->
            model.getBinaryCode(
                opcode,
                "indirect",
                evalExpr(amCtx.am_index_indirect().address().expr()) + evalExpr(amCtx.am_index_indirect().num().expr()),
                0
            )
        else -> {
            println("unknown addressing mode")
            exitProcess(1)
        }
    }

    var s = String.format("[%06x]%02x:%04x", proc.romPage * 32 * 1024 + addr, proc.romPage, addr)
    s = s.padEnd(20, ' ')
    for (i in 0 until bytes.size) {
        s += String.format("%02x ", bytes[i])
    }
    s = s.padEnd(35, ' ')
    s += proc.romLabelRefMap.getOrDefault(addr, emptyList<String>()).joinToString(",")
    s = s.padEnd(55, ' ')
    s += " " + opcode + " " + amCtx.text
    if (lineCtx.COMMENT() != null) {
        s = s.padEnd(80, ' ')
        s += lineCtx.COMMENT().text
    }
    output_list(s)

    return bytes
}

private fun asm_proc(proc: Proc) {
    proc.rom.clear()

    proc.romLabelOffsetMap.forEach { label, offset ->
        proc.romLabelRefMap.getOrPut(proc.romAddr + offset, { mutableListOf() }).add(label)
    }

    var addr = proc.romAddr
    for (lineCtx in proc.lineCtxs) {
        try {
            val bytes = asm_proc_line(proc, lineCtx, addr)
            addr += bytes.size
            bytes.forEach { proc.rom.add(0xff and it.toInt()) }
        } catch (e: Exception) {
            println(lineCtx.text)
            e.printStackTrace()
            exitProcess(1)
        }
    }
}

private fun parse_bss(progCtxs: List<ASM74Parser.ProgContext>): MutableList<BSS> {
    val bsses = mutableListOf<BSS>()

    var bss = BSS(null)
    for (progCtx in progCtxs) {
        var isBssSegment = false
        for (lineCtx in progCtx.line()) {
            if (isBssSegment && lineCtx.label() != null) {
                bss.ramLabelOffsetMap[lineCtx.label().ID().text] = bss.ramSize
            }

            if (lineCtx.cmd() != null) {
                val cmdCtx = lineCtx.cmd()
                val cmd = cmdCtx.start.text
                if (cmd == ".segment") {
                    isBssSegment = lineCtx.cmd().STRING().text.trim('"').toUpperCase() == "BSS"
                    if (isBssSegment) {
                        bss = BSS(progCtx)
                        bsses.add(bss)
                    }
                }

                if (isBssSegment) {
                    if (cmd == ".res") {
                        bss.ramSize += str2int(lineCtx.cmd().NUMBER(0).text)
                    }
                }
            }
        }
    }

    return bsses
}

private fun parse_data(progCtxs: List<ASM74Parser.ProgContext>): List<Data> {
    val datas = mutableListOf<Data>()

    var data = Data(null)
    var label = ""
    for (progCtx in progCtxs) {
        var isDataSegment = false
        for (lineCtx in progCtx.line()) {
            if (isDataSegment && lineCtx.label() != null) {
                label = lineCtx.label().ID().text
                data.romLabelOffsetMap["_init_$label"] = data.romSize
                data.ramLabelOffsetMap[label] = data.ramSize
                data.romLabelSizeMap[label] = 0
            }

            if (lineCtx.cmd() != null) {
                val cmdCtx = lineCtx.cmd()
                val cmd = cmdCtx.start.text
                if (cmd == ".segment") {
                    isDataSegment = lineCtx.cmd().STRING().text.trim('"').toUpperCase() == "DATA"
                    if (isDataSegment) {
                        data = Data(progCtx)
                        datas.add(data)
                    }
                }

                if (isDataSegment) {
                    when (cmd) {
                        ".byte" -> {
                            val bytes = cmdCtx.NUMBER().map { str2int(it.text) }
                            bytes.forEach {
                                data.rom.add(0xff and it)
                            }
                            data.romSize += bytes.size
                            data.ramSize += bytes.size
                            data.romLabelSizeMap[label] = data.romLabelSizeMap[label]!! + bytes.size
                        }
                        ".word" -> {
                            val words = cmdCtx.NUMBER().map { str2int(it.text) }
                            words.forEach {
                                data.rom.add(0xff and it)
                                data.rom.add(0xff and (it shr 8))
                            }
                            data.romSize += words.size * 2
                            data.ramSize += words.size * 2
                            data.romLabelSizeMap[label] =
                                data.romLabelSizeMap[label]!! + words.size * 2
                        }
                        ".dword" -> {
                            val dwords = cmdCtx.NUMBER().map { str2int(it.text) }
                            dwords.forEach {
                                data.rom.add(0xff and it)
                                data.rom.add(0xff and (it shr 8))
                                data.rom.add(0xff and (it shr 16))
                                data.rom.add(0xff and (it shr 24))
                            }
                            data.romSize += dwords.size * 4
                            data.ramSize += dwords.size * 4
                            data.romLabelSizeMap[label] =
                                data.romLabelSizeMap[label]!! + dwords.size * 4
                        }
                        ".addr" -> {
                            // TODO 这里没处理函数指针的情况
                            data.romLabelRefMap[data.romSize] = cmdCtx.ID().text
                            data.ramSize += 2
                            data.romSize += 2
                            data.rom.add(0)
                            data.rom.add(0)
                            data.romLabelSizeMap[label] = data.romLabelSizeMap[label]!! + 2
                        }
                        ".res" -> {
                            val size = str2int(lineCtx.cmd().NUMBER(0).text)
                            data.ramSize += size
                        }
                    }
                }
            }
        }
    }

    return datas
}

private fun parse_proc(progCtxs: List<ASM74Parser.ProgContext>): List<Proc> {
    val procs = mutableListOf<Proc>()

    var proc = Proc(null, "")
    for (progCtx in progCtxs) {
        var isCodeSegment = false
        for (lineCtx in progCtx.line()) {
            if (isCodeSegment && lineCtx.label() != null) {
                val name = lineCtx.label().ID().text
                proc.romLabelOffsetMap[name] = proc.romSize
            }

            if (lineCtx.cmd() != null) {
                val cmdCtx = lineCtx.cmd()
                val cmd = cmdCtx.start.text
                if (cmd == ".segment") {
                    isCodeSegment = lineCtx.cmd().STRING().text.trim('"').toUpperCase() == "CODE"
                }

                if (isCodeSegment && cmd == ".proc") {
                    val name = cmdCtx.ID().text
                    if (procs.any { it.name == name }) {
                        println("Duplicated proc: $name")
                        exitProcess(1)
                    }
                    proc = Proc(progCtx, name)
                    proc.romLabelOffsetMap[name] = 0

                    procs.add(proc)
                }

                if (isCodeSegment && cmd == "debug") {
                    proc.lineCtxs.add(lineCtx);
                }
            }

            if (isCodeSegment && lineCtx.opcode() != null) {
                var amName = getAmName(lineCtx.am())
                if (amName == "direct_index") amName = "direct"
                else if (amName == "index_indirect") amName = "indirect"

                proc.lineCtxs.add(lineCtx)
                proc.romSize += model.getInstructionLength(lineCtx.opcode().text, amName)
            }
        }
    }

    return procs
}

private fun parse_rodata(progCtxs: List<ASM74Parser.ProgContext>): List<ROData> {
    val rodatas = mutableListOf<ROData>()

    var rodata = ROData(null)
    for (progCtx in progCtxs) {
        var isReadOnlyDataSegment = false
        for (lineCtx in progCtx.line()) {
            if (isReadOnlyDataSegment && lineCtx.label() != null) {
                rodata.ramLabelOffsetMap[lineCtx.label().ID().text] = rodata.romSize
            }

            if (lineCtx.cmd() != null) {
                val cmdCtx = lineCtx.cmd()
                val cmd = cmdCtx.start.text
                if (cmd == ".segment") {
                    isReadOnlyDataSegment = lineCtx.cmd().STRING().text.trim('"').toUpperCase() == "RODATA"
                    if (isReadOnlyDataSegment) {
                        rodata = ROData(progCtx)
                        rodatas.add(rodata)
                    }
                }

                if (isReadOnlyDataSegment) {
                    if (cmd == ".byte") {
                        val bytes = cmdCtx.NUMBER().map { str2int(it.text) }
                        rodata.romSize += bytes.size
                        rodata.ramSize += bytes.size

                        bytes.forEach { rodata.rom.add(0xff and it) }
                    } else if (cmd == "=") {
                        val refAddr = evalExpr(cmdCtx.expr(), varMap = rodata.ramLabelOffsetMap)
                        rodata.ramLabelOffsetMap[lineCtx.label().ID().text] = refAddr
                    }
                }
            }
        }
    }

    return rodatas
}

private fun antlr_parse(content: String): ASM74Parser.ProgContext {
    val errorLister = object : BaseErrorListener() {
        override fun syntaxError(
            recognizer: Recognizer<*, *>?,
            offendingSymbol: Any?,
            line: Int,
            charPositionInLine: Int,
            msg: String?,
            e: RecognitionException?
        ) {
            System.err.println("line $line:$charPositionInLine $msg")
            exitProcess(1)
        }
    }
    val lexer = ASM74Lexer(ANTLRInputStream(content))
    lexer.addErrorListener(errorLister)
    val token = CommonTokenStream(lexer)
    val parser = ASM74Parser(token)
    parser.addErrorListener(errorLister)
    return parser.prog()
}

private fun output_list(str: String, cr: Boolean = true) {
    fos?.write(str.toByteArray(charset("UTF-8")))
    if (cr) {
        fos?.write('\n'.toInt())
    }

    print(str)
    if (cr) {
        println()
    }
}

private fun gen_ljmp_data(): String {
    val code_ljmp = """
        .segment "CODE"
        .proc longjmp
        lda TMP0,#2
        sta PAGE
        jmp (TMP0)
        
    """.trimIndent()
    val proc_ljmp = parse_proc(listOf(antlr_parse(code_ljmp)))[0]
    asm_proc(proc_ljmp)
    val hex = proc_ljmp.rom.fold("", { last, value ->
        last + if (last == "") {
            String.format("0x%02X", value)
        } else {
            ", ${String.format("0x%02X", value)}"
        }
    })

    val data_ljmp = """
        .segment "DATA"
        longjmp: .byte $hex
    
    """.trimIndent()

    return data_ljmp
}

private fun hexdump(name: String, rom: Array<Int>, range: Pair<Int, Int>) {
    output_list("    \t==================== $name ===================")
    output_list("    \t 0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F")

    var n = 0
    val bytes = ByteArray(16)
    for (addr in range.first until range.second) {
        if (n == 0) {
            output_list("%04x\t".format(addr), false)
        }
        bytes[n % 16] = if (rom[addr] < 0x20) '.'.toByte() else rom[addr].toByte()
        output_list("%02x ".format(rom[addr]), false)

        n++
        if (n == 16) {
            n = 0
            output_list("\t${bytes.toString(charset("UTF-8"))}")
        }
    }
    if (n != 0) {
        output_list("${"".padEnd(3 * (16 - n))}\t${bytes.copyOf(n).toString(charset("UTF-8"))}")
    }
}

fun gen_bp(lst: String) {
    val bp = lst + "_bp.bin"

    val rom = Array(2 shl 24) { 0 }

    for (line in Files.readAllLines(Paths.get(lst))) {
        if (line.contains(';')) {
            val addr = line.substring(1..6).toInt(16)
            rom[addr] = 1
        }
    }

    Files.write(Paths.get(bp), rom2logisim(rom).toByteArray(charset("UTF-8")))
}

fun main(args: Array<String>) {
//    val afiles = arrayOf("../hello.s")
//    val afiles = arrayOf("../cc74/test/inc.s")
//    val afiles = arrayOf(
//        "../tinybasic/str.s",
//        "../tinybasic/io.s",
//        "../tinybasic/code.s",
//        "../tinybasic/lexer.s",
//        "../tinybasic/interpreter.s",
//        "../tinybasic/mxpbasic.s"
//    )
    val afiles = arrayOf("../mxpbasic/mxpbasic.s")
//    val afiles = arrayOf("/home/maxupeng/8bitcpu/8bitcpu/asm74/src/test/resources/echo.s");
    val files = afiles.map { File(it).absolutePath }.sorted()


    val dir = File(files[0]).parent
    val dst = if (files.size == 1) files[0].replaceAfterLast('.', "bin", "$dir/out.bin") else "$dir/out.bin"
    val lst = if (files.size == 1) files[0].replaceAfterLast('.', "alst", "$dir/out.alst") else "$dir/out.alst"

    fos = FileOutputStream(lst)

    globalLabelAddrMap["A"] = 0x8000
    globalLabelAddrMap["B"] = 0x8004
    globalLabelAddrMap["SP"] = 0x8008
    globalLabelAddrMap["TMP0"] = 0x8010
    globalLabelAddrMap["TMP1"] = 0x8020
    globalLabelAddrMap["TMP2"] = 0x8030
    globalLabelAddrMap["TMP3"] = 0x8040
    globalLabelAddrMap["TMP4"] = 0x8050
    globalLabelAddrMap["STACK_BASE"] = 0x9FFF
    globalLabelAddrMap["PAGE"] = 0xFFFF

    val progCtxs = mutableListOf<ASM74Parser.ProgContext>()
    for (file in files) {
        val content = Files.readAllBytes(Paths.get(file)).toString(charset("UTF-8"))
        progCtxs.add(antlr_parse(content))
    }

    // _ljmp in memory
    progCtxs.add(0, antlr_parse(gen_ljmp_data()))
    val rodatas = parse_rodata(progCtxs)

    val datas = parse_data(progCtxs)
    val bsses = parse_bss(progCtxs)
    val procs = parse_proc(progCtxs)


    // 分页
    var page = 0
    var length = 0
    for (proc in procs) {
        if (proc.romSize >= ROM_PAGE_SIZE) {
            println("函数大小超过$ROM_PAGE_SIZE：${proc.name}")
            exitProcess(1)
        }

        if (length + proc.romSize >= ROM_PAGE_SIZE) {
            page++
            length = 0
        }

        proc.romPage = page
        proc.romAddr = ROM_PAGE_START + length
        length += proc.romSize

        setLabelPageAddr(proc.progCtx!!, proc.name, proc.romPage, proc.romAddr)
        proc.romLabelOffsetMap.forEach { (label, offset) ->
            setLabelPageAddr(proc.progCtx!!, label, proc.romPage, proc.romAddr + offset)
        }
    }


    var romAddr = ROM_FIX_START
    var romAddrMark = romAddr

    var ramAddr = RAM_DATA_START
    var ramAddrMark = ramAddr

    romAddrMark = romAddr;

    // _init
    val code_init = """
        .segment "CODE"
        .proc _init
        lda #L(STACK_BASE)
        sta SP
        lda #H(STACK_BASE)
        sta SP,#1
        
    """.trimIndent()

    val proc_init = parse_proc(listOf(antlr_parse(code_init)))[0]
    proc_init.romAddr = romAddr
    romAddr += proc_init.romSize

    // _init_rodata
    var rodata_size = 0
    rodatas.forEach { rodata ->
        rodata_size += rodata.romSize
    }
    val code_init_rodata = """
        .segment "CODE"
        .proc _init_rodata
        lda #${rodata_size and 0xff}
        sta TMP0
        lda #${(rodata_size shr 8) and 0xff}
        sta TMP0,#1
        lda #L(rodata_rom_addr)
        sta TMP1
        lda #H(rodata_rom_addr)
        sta TMP1,#1
        lda #L(rodata_ram_addr)
        sta TMP2
        lda #H(rodata_ram_addr)
        sta TMP2,#1
_init_rodata0: 
        lda TMP0
        or TMP0,#1
        jz _init_rodata1
        lda (TMP1)
        sta (TMP2)
        sec
        lda TMP0
        sub #1
        sta TMP0
        lda TMP0,#1
        sub #0
        sta TMP0,#1
        clc
        lda TMP1
        add #1
        sta TMP1
        lda TMP1,#1
        add #0
        sta TMP1,#1
        clc
        lda TMP2
        add #1
        sta TMP2
        lda TMP2,#1
        add #0
        sta TMP2,#1
        jmp _init_rodata0
_init_rodata1:

    """.trimIndent()

    val proc_init_rodata = parse_proc(listOf(antlr_parse(code_init_rodata)))[0]
    proc_init_rodata.romAddr = romAddr
    romAddr += proc_init_rodata.romSize

    proc_init_rodata.romLabelOffsetMap.forEach {label, offset ->
        setLabelPageAddr(null, label, 0, proc_init_rodata.romAddr + offset);
    }

    // _init_data
    datas.forEach { data ->
        data.genInitProc()
        data.initProc!!.romAddr = romAddr
        romAddr += data.initProc!!.romSize

        val proc = data.initProc!!
        setLabelPageAddr(proc.progCtx!!, proc.name, proc.romPage, proc.romAddr)
        data.initProc!!.romLabelOffsetMap.forEach { label, offset ->
            setLabelPageAddr(
                proc.progCtx!!,
                label,
                proc.romPage,
                proc.romAddr + offset
            )
        }
    }

    // jmp _main
    val code_jmp_main = """
        .segment "CODE"
        .proc _jmp_main
        lda #L(_main)
        sta TMP0
        lda #H(_main)
        sta TMP0,#1
        lda #P(_main)
        sta TMP0,#2
        jmp longjmp
        
    """.trimIndent()
    val proc_jmp_main = parse_proc(listOf(antlr_parse(code_jmp_main)))[0]
    proc_jmp_main.romAddr = romAddr

    romAddr += proc_jmp_main.romSize

    rom_code = Pair(romAddrMark, romAddr);


    romAddr += 0x10 - (romAddr and 0xf)     // 对齐

    // rodata
    romAddrMark = romAddr
    ramAddrMark = ramAddr

    setLabelPageAddr(null, "rodata_rom_addr", 0, romAddr);
    setLabelPageAddr(null, "rodata_ram_addr", 0, ramAddr);
    rodatas.forEach { rodata ->
        rodata.romAddr = romAddr
        romAddr += rodata.romSize

        rodata.ramAddr = ramAddr;
        ramAddr += rodata.romSize;

        rodata.ramLabelOffsetMap.forEach { label, offset ->
            setLabelPageAddr(rodata.progCtx, label, 0, rodata.ramAddr + offset)
        }
    }
    rom_rodata = Pair(romAddrMark, romAddr)
    ram_rodata = Pair(ramAddrMark, ramAddr)



    romAddr += 0x10 - (romAddr and 0xf)     // 对齐

    // data default value
    ramAddrMark = ramAddr
    romAddrMark = romAddr
    datas.forEach { data ->
        data.romAddr = romAddr
        romAddr += data.romSize

        data.romLabelOffsetMap.forEach { label, offset ->
            setLabelPageAddr(data.progCtx, label, data.romPage, data.romAddr + offset)
        }

        data.ramAddr = ramAddr
        ramAddr += data.ramSize

        data.ramLabelOffsetMap.forEach { label, offset ->
            setLabelPageAddr(data.progCtx, label, data.ramPage, data.ramAddr + offset)
        }
    }
    ram_data = Pair(ramAddrMark, ramAddr)
    rom_data = Pair(romAddrMark, romAddr)

    // bss
    ramAddrMark = ramAddr
    bsses.forEach { bss ->
        bss.ramAddr = ramAddr
        ramAddr += bss.ramSize

        bss.ramLabelOffsetMap.forEach { label, offset ->
            setLabelPageAddr(bss.progCtx, label, bss.ramPage, bss.ramAddr + offset)
        }
    }
    ram_bss = Pair(ramAddrMark, ramAddr)

    // 到此，所有的 label 地址都确认完毕，开始使用替换原有的占位符
    datas.forEach { data ->
        data.romLabelRefMap.forEach { offset, label ->
            val addr = getLabelAddr(data.progCtx, label)
            data.rom[offset] = addr and 0xff
            data.rom[offset + 1] = (addr shr 8) and 0xff
        }
    }



    // 汇编
    asm_proc(proc_init)
    asm_proc(proc_init_rodata)
    datas.forEach { data ->
        asm_proc(data.initProc!!)
    }
    asm_proc(proc_jmp_main)

    procs.forEach { proc ->
        asm_proc(proc)
    }

    // 生成 ROM
    val rom = Array(2 shl 24) { 0 }

    proc_init.output_rom(rom)
    proc_init_rodata.output_rom(rom)
    proc_jmp_main.output_rom(rom)

    datas.forEach { it.output_rom(rom) }
    rodatas.forEach { it.output_rom(rom) }
    procs.forEach { it.output_rom(rom) }



    hexdump("RODATA", rom, rom_rodata)
    hexdump("DATA", rom, rom_data)

    val segmentInfo = """
        
    ROM CODE :[0x${rom_code.first.toString(16)}, 0x${rom_code.second.toString(16)}) ${rom_code.second - rom_code.first}
    ROM RODATA :[0x${rom_rodata.first.toString(16)}, 0x${rom_rodata.second.toString(16)}) ${rom_rodata.second - rom_rodata.first}
    ROM DATA :[0x${rom_data.first.toString(16)}, 0x${rom_data.second.toString(16)}) ${rom_data.second - rom_data.first}

    RAM RODATA :[0x${ram_rodata.first.toString(16)}, 0x${ram_rodata.second.toString(16)}) ${ram_rodata.second - ram_rodata.first}
    RAM DATA :[0x${ram_data.first.toString(16)}, 0x${ram_data.second.toString(16)}) ${ram_data.second - ram_data.first}
    RAM BSS :[0x${ram_bss.first.toString(16)}, 0x${ram_bss.second.toString(16)}) ${ram_bss.second - ram_bss.first}

    """.trimIndent()
    println(segmentInfo)

//    globalLabelAddrMap.forEach { label, addr ->
//        println("$label:\t${String.format("%04x", addr)}")
//    }
    datas.forEach { data ->
        data.ramLabelOffsetMap.forEach { label, offset ->
            println("$label:${Integer.toHexString(data.ramAddr+offset)}:8")
        }
    }
    bsses.forEach { bss ->
        bss.ramLabelOffsetMap.forEach { label, offset ->
            println("$label:${Integer.toHexString(bss.ramAddr+offset)}:8")
        }
    }

    /*
    * ROM
        * $0000-$3FFF   16K
            * init
            * rodata
            * data init value
        * $4000-$7FFF   16K
            * rom page
    * ram
        * $8000-$80FF   编译器占用
        * $8100-$9FFF   8K stack
        * $A000-$BFFF   8K data
           * jmp _main
           * rodata
           * data
           * bss
        * $C000-$FFEF   16K heap
        * $FFF0-$FFFF   IO
    */
    if (ram_bss.second > RAM_DATA_END) {
        println("RAM空间不足");
    }

    if (rom_data.second > ROM_FIX_END) {
        println("ROM空间不足")
    }

    val output = rom2logisim(rom)
    Files.write(Paths.get(dst), output.toByteArray(charset("UTF-8")))

    fos?.close()

    gen_bp(lst)
}